﻿using CommonTools;
using System.Collections.Concurrent;
using YukariToolBox.LightLog;

namespace Discord;

/// <summary>
/// 任务队列
/// </summary>
public class TaskQueue
{
    #region 属性

    /// <summary>
    /// 执行队列 
    /// </summary>
    private readonly ConcurrentDictionary<string, ConcurrentQueue<Task>> _waitQueue;

    /// <summary>
    /// 记录队列
    /// </summary>
    private readonly ConcurrentQueue<string> _concurQueue;

    /// <summary>
    /// 等待队列数
    /// </summary>
    private readonly int _waitSize;

    /// <summary>
    /// 并发数
    /// </summary>
    private readonly int _concurSize;

    /// <summary>
    /// 队列类
    /// </summary>
    private static TaskQueue? taskQueue;

    #endregion

    #region 私有构造函数

    private TaskQueue(int concurSize, int waitSize)
    {
        _concurSize = concurSize;
        _waitSize = waitSize;
        _waitQueue = new ConcurrentDictionary<string, ConcurrentQueue<Task>>();
        _concurQueue = new ConcurrentQueue<string>();
    }

    #endregion

    #region 队列方法

    /// <summary>
    /// 追加队列
    /// </summary>
    /// <param name="triggerId"></param>
    /// <param name="action"></param>
    /// <param name="args"></param>
    public void Put(string triggerId, Func<string, ValueTask<bool>> action, string args)
    {
        if (_waitQueue.Count >= _waitSize)
        {
            Log.Debug("TaskQueue", $"Task queue is full: {_waitSize}");
            return;
        }

        var queue = new ConcurrentQueue<Task>();
        queue.Enqueue(Task.Run(async () =>
        {
            try
            {
                await action(args);
            }
            catch (Exception ex)
            {
                Log.Error("TaskQueue", $"Task[{triggerId}] execution failed");
            }
        }));
        _waitQueue.TryAdd(triggerId, queue);
        ExecuteTasks();
    }

    /// <summary>
    /// 追加队列
    /// </summary>
    /// <param name="triggerId"></param>
    /// <param name="action"></param>
    /// <param name="index"></param>
    /// <param name="msgId"></param>
    /// <param name="msgHash"></param>
    public void Put(string triggerId, Func<int, string, string, ValueTask<bool>> action, int index, string msgId, string msgHash)
    {
        if (_waitQueue.Count >= _waitSize)
        {
            Log.Debug("TaskQueue", $"Task queue is full: {_waitSize}");
            return;
        }

        var queue = new ConcurrentQueue<Task>();
        queue.Enqueue(Task.Run(async () =>
        {
            try
            {
                await action(index, msgId, msgHash);
            }
            catch (Exception ex)
            {
                Log.Error("TaskQueue", $"Task[{triggerId}] execution failed");
            }
        }));
        _waitQueue.TryAdd(triggerId, queue);
        ExecuteTasks();
    }


    /// <summary>
    /// 追加队列
    /// </summary>
    /// <param name="triggerId"></param>
    /// <param name="action"></param>
    /// <param name="msgId"></param>
    /// <param name="msgHash"></param>
    public void Put(string triggerId, Func<string, string, ValueTask<bool>> action, string msgId, string msgHash)
    {
        if (_waitQueue.Count >= _waitSize)
        {
            Log.Debug("TaskQueue", $"Task queue is full: {_waitSize}");
            return;
        }

        var queue = new ConcurrentQueue<Task>();
        queue.Enqueue(Task.Run(async () =>
        {
            try
            {
                await action(msgId, msgHash);
            }
            catch (Exception ex)
            {
                Log.Error("TaskQueue", $"Task[{triggerId}] execution failed");
            }
        }));
        _waitQueue.TryAdd(triggerId, queue);
        ExecuteTasks();
    }

    /// <summary>
    /// 追加队列
    /// </summary>
    /// <param name="triggerId"></param>
    /// <param name="action"></param>
    /// <param name="msgId"></param>
    /// <param name="msgHash"></param>
    /// <param name="direction"></param>
    public void Put(string triggerId, Func<string, string,string, ValueTask<bool>> action, string msgId, string msgHash,string direction)
    {
        if (_waitQueue.Count >= _waitSize)
        {
            Log.Debug("TaskQueue", $"Task queue is full: {_waitSize}");
            return;
        }

        var queue = new ConcurrentQueue<Task>();
        queue.Enqueue(Task.Run(async () =>
        {
            try
            {
                await action(msgId, msgHash, direction);
            }
            catch (Exception ex)
            {
                Log.Error("TaskQueue", $"Task[{triggerId}] execution failed");
            }
        }));
        _waitQueue.TryAdd(triggerId, queue);
        ExecuteTasks();
    }

    /// <summary>
    /// 清楚队列
    /// </summary>
    /// <param name="triggerId"></param>
    public void Pop(string triggerId)
    {
        _concurQueue.ToList().Remove(triggerId);
        if (_waitQueue.Count > 0)
            ExecuteTasks();
    }

    /// <summary>
    /// 执行队列
    /// </summary>
    private void ExecuteTasks()
    {
        while (_concurQueue.Count < _concurSize && _waitQueue.Any())
        {
            var result = _waitQueue.LastOrDefault();
            _waitQueue.Remove(result.Key, out var taskInfo);
            if (taskInfo != null)
            {
                taskInfo.TryDequeue(out var task);
                _concurQueue.Enqueue(result.Key); // unique key for each task in concur queue
                Log.Debug("TaskQueue", $"Task[{_concurQueue.Last()}] start execution: {task}");
                if (task != null)
                {
                    task.ContinueWith((t) =>
                    {
                        var key = _concurQueue.Last();
                        _concurQueue.TryDequeue(out var _); // Remove the key from concur queue once the task is completed.
                        if (t.IsFaulted)
                        {
                            Log.Error("TaskQueue", $"Task[{key}] execution failed {t.Exception}");
                        }
                    });
                }
            }

        }
    }

    /// <summary>
    /// 获取并发数
    /// </summary>
    /// <returns></returns>
    public int ConcurSize() => _concurSize;

    /// <summary>
    /// 获取等待队列数
    /// </summary>
    /// <returns></returns>
    public int WaitSize() => _waitSize;

    /// <summary>
    /// 清除等待队列
    /// </summary>
    public void ClearWait() => _waitQueue.Clear();

    /// <summary>
    /// 清除并发队列
    /// </summary>
    public void ClearConcur() => _concurQueue.Clear();


    #endregion

    #region 单例

    public static TaskQueue Instance
    {
        get
        {
            if (!int.TryParse(AppConfigurationServices.Configuration["DiscordSetting:CONCUR_SIZE"], out int concurSize))
            {
                concurSize = 999;
            }

            if (!int.TryParse(AppConfigurationServices.Configuration["DiscordSetting:WAIT_SIZE"], out int waitSize))
            {
                waitSize = 999;
            }

            return taskQueue ??= new TaskQueue(concurSize, waitSize);
        }
    }

    #endregion

}